package com.hoshiicloud.shxping.controller;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.hoshiicloud.common.controller.BaseController;
import com.hoshiicloud.common.exception.ServiceException;
import com.hoshiicloud.common.rpc.Response;
import com.hoshiicloud.common.utils.AESUtil;
import com.hoshiicloud.common.utils.SMSCodeUtil;
import com.hoshiicloud.market.entity.market.ShareInfo;
import com.hoshiicloud.shxping.dto.*;
import com.hoshiicloud.shxping.feign.*;
import com.hoshiicloud.shxping.param.CodeLoginParam;
import com.hoshiicloud.shxping.param.MemberWithConsumerParam;
import com.hoshiicloud.shxping.param.TTLoginParam;
import com.hoshiicloud.shxping.param.WXLoginParam;
import com.hoshiicloud.shxping.service.ILoginService;
import com.hoshiicloud.shxping.util.WXCore;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * C端用户登录相关操作Controller类
 *
 * @author : yangcheng
 * @date : 2019/5/13 19:16
 * @mail : maiboyc@163.com
 **/
@Slf4j
@RestController
@RequestMapping("/login")
@Api(value = "登录管理", tags = {"登录相关操作接口"}, produces = MediaType.APPLICATION_JSON_VALUE)
public class LoginController extends BaseController {

    // 微信小程序appId
    @Value("${weChat.app-id}")
    private String APPID;

    // 微信小程序SECRET
    @Value("${weChat.app-secret}")
    private String SECRET;

    // 微信小程序appId
    @Value("${touTiao.app-id}")
    private String TTAPPID;

    // 微信小程序SECRET
    @Value("${touTiao.app-secret}")
    private String TTSECRET;

    // 短信验证码失效时间
    @Value("${sms.expireSeconds}")
    private Long expireSeconds;

    // 登录相关Service
    @Resource
    private ILoginService loginService;

    // 验证中心微服务Service
    @Resource
    private VeriCenterFeignService veriCenterFeignService;

    // 用户域微服务FeignClient
    @Autowired
    private IUserFeignService userFeignService;

    // redisTemplate
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    // 营销微服务FeignClient
    @Resource
    private IMarketFeignService marketFeignService;

    // id生成服务
    @Resource
    private IdGeneratorService idGeneratorService;

    // 文件服务FeignClient
    @Autowired
    private IFileServerFeignClient fileServerFeignClient;

    // 商品服务FeignClient
    @Autowired
    private GoodsFeignService goodsFeignService;

    /**
     * 发送验证码
     * @param mobile 手机号码
     * @return Response
     */
    @GetMapping("/{mobile}")
    @ApiOperation(value = "发送手机验证码", notes = "根据手机号码发送验证码", consumes = MediaType.ALL_VALUE)
    public Response verifCode(@PathVariable("mobile") String mobile) {
        Response<String> rs;
        try {
            // 生成手机6位验证码
            String code = SMSCodeUtil.code(4);
            Response send_rs = veriCenterFeignService.sendCode(mobile,code,expireSeconds);
            if (null != send_rs && send_rs.getCode() == 200 && null != send_rs.getData()) {
                rs = Response.data((String)send_rs.getData());
            } else {
                rs = Response.failed("短信验证码发送失败");
            }
        } catch (Exception e) {
            log.error("根据手机号码发送验证码异常"+"["+e.getMessage()+"]");
            rs = Response.failed(e.getMessage());
        }
        return rs;
    }

    /**
     * 手机号+验证码登录(手机号+验证码注册)
     * @param param     手机验证码登录参数
     * @return Response
     */
    @PostMapping("/loginByCode")
    @ApiOperation(value = "手机号+验证码登录", notes = "手机号+验证码登录", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response codeLogin(@RequestBody @Valid CodeLoginParam param, BindingResult result) {
        Response rs;
        try {
            if (CollectionUtils.isNotEmpty(handleValidMsg(result))){// 验证不通过
                rs = Response.failed(JSON.toJSONString(handleValidMsg(result)));
            } else {// 验证通过
                Response check_rs = veriCenterFeignService.checkCode(param.getMsgId(),param.getCode());
                if (null != check_rs && check_rs.getCode() == 200 && "验证成功".equals(check_rs.getMsg())) {// 验证短信验证码成功，插入会员记录
                    rs = loginService.codeLogin(param.getCompanyId(), param.getPlatformId(), param.getMobile(),param.getInvitationCode());
                } else {// 验证失败,将验证信息返回
                    rs = Response.failed("登录失败："+ (check_rs != null ? check_rs.getMsg() : ""));
                }
            }
        } catch (ServiceException e) {
            log.error("根据手机号码和验证码登录异常"+"["+e.getMessage()+"]");
            rs = Response.failed("登录失败："+e.getError());
        } catch (Exception e) {
            log.error("根据手机号码和验证码登录异常"+"["+e.getMessage()+"]");
            rs = Response.failed("登录失败："+"["+e.getMessage()+"]");
        }
        return rs;
    }

    /**
     * 微信登录(用户在app端点击微信登录调用微信开放平台授权登录，拿到信息以后需要存入到当前的会员信息表中)
     * @param param     微信登录参数
     * @return Response
     */
    @PostMapping("/loginByWeChat")
    @ApiOperation(value = "微信登录", notes = "使用微信登录", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response loginByWeChat(@RequestBody @Valid WXLoginParam param) {
        Response rs;
        try {
            String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+APPID+"&secret="+SECRET+"&js_code="+ param.getCode() +"&grant_type=authorization_code";
            RestTemplate restTemplate = new RestTemplate();
            //进行网络请求,访问url接口
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
            //根据返回值进行后续操作
            if(responseEntity.getStatusCode() == HttpStatus.OK) {
                String sessionData = responseEntity.getBody();
                //解析从微信服务器获得的openid和session_key;
                WeChatSession weChatSession = JSON.parseObject(sessionData,WeChatSession.class);
                //获取用户的唯一标识
                String openid = weChatSession.getOpenid();
                if(StringUtils.isEmpty(openid)){
                    rs = Response.failed("请求微信接口失败");
                    log.error("请求微信接口失败:"+sessionData);
                }else{
                    //获取会话秘钥
                    String session_key = weChatSession.getSession_key();
                    //最后要返回一个自定义的登录态,用来做后续数据传输的验证
                    rs = loginService.wxLogin(param.getCompanyId(),param.getPlatformId(),param.getInvitationCode(),param.getNickname(),param.getSex(),param.getProvince(),param.getCity(),param.getCountry(),param.getHeadimgurl(),session_key,openid,APPID,param.getEncryptedData(),param.getIv());
                }
            } else {
                rs = Response.failed("请求微信接口失败");
                log.error("请求微信接口失败");
            }
        } catch (Exception e) {
            log.error("微信登录出现异常"+"["+e.getMessage()+"]");
            rs = Response.failed("微信登录出现异常"+"["+e.getMessage()+"]");
        }
        return rs;
    }

    /**
     * 小程序微信已经登录过不需要再登录
     * @param param 参数{"companyId":1,"platformId":2,"code":"1345"}
     * @return
     */
    @PostMapping("/loginByWeChatResident")
    @ApiOperation(value = "微信常驻登录", notes = "使用微信常驻登录", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response<Map<String,Object>> loginByWeChatResident(@RequestBody Map<String,Object> param) {
        Response<Map<String,Object>> rs;
        try {
            String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+APPID+"&secret="+SECRET+"&js_code="+ param.get("code") +"&grant_type=authorization_code";
            RestTemplate restTemplate = new RestTemplate();
            // 进行网络请求,访问url接口
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
            // 根据返回值进行后续操作
            if(responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
                String sessionData = responseEntity.getBody();
                // 解析从微信服务器获得的openid和session_key;
                WeChatSession weChatSession = JSON.parseObject(sessionData,WeChatSession.class);
                // 获取用户的唯一标识
                String openid = weChatSession.getOpenid();
                // 获取会话秘钥
                String session_key = weChatSession.getSession_key();
                if (StringUtils.isNotEmpty(openid)) {
                    MemberWithConsumerParam map = new MemberWithConsumerParam();
                    map.setCompanyId(Long.parseLong((String)param.get("companyId")));// 租户ID
                    map.setPlatformId(Long.parseLong((String)param.get("platformId")));// 平台ID
                    map.setWxOpenId(openid);// wxOpenid
                    Response<Map<String,Object>> info_rs = loginService.getMemberWithConsumerInfo(map);
                    if (null == info_rs.getData()) {
                        rs = Response.failed("当前微信没有注册过，请重新注册登录");
                    } else {
                        rs = info_rs;
                    }
                } else {
                    rs = Response.failed(responseEntity.getBody());
                }
            } else {
                rs = Response.failed("请求微信接口失败");
            }
        } catch (Exception e) {
            log.error("微信登录出现异常"+"["+e.getMessage()+"]");
            rs = Response.failed();
        }
        return rs;
    }

    /**
     * 获取用户加密码
     * @param userId 用户ID
     * @return Response<String>
     */
    @GetMapping("/getUserEncryptCode/{userId}")
    @ApiOperation(value = "获取用户加密码", notes = "获取用户加密码，用来生成二维码", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response<String> getUserEncryptCode(@PathVariable("userId") Long userId) {
        Response<ConsumerDto> rs = userFeignService.getConsumerById(userId);
        if (null != rs && rs.getCode() == 200 && null != rs.getData()) {
            return Response.data(AESUtil.encrypt(rs.getData().getId()+":"+rs.getData().getMemberId()+":"+rs.getData().getInviteCode()));
        } else {
            return Response.failed("获取用户加密码出错！");
        }
    }

    /**
     * 获取微信接口访问AccessToken
     * @return Response<String>
     */
    @GetMapping("/getWeChatAccessToken")
    @ApiOperation(value = "获取微信接口访问AccessToken", notes = "获取微信接口访问AccessToken", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response<String> getWeChatAccessToken() {
        Response<String> rs;
        try {
            log.info("获取微信接口访问AccessToken");
            rs = WXCore.getAccessToken(redisTemplate,APPID,SECRET);
        } catch (Exception e) {
            log.error("获取微信接口访问AccessToken异常："+e.getMessage());
            rs = Response.failed("获取微信接口访问AccessToken异常："+e.getMessage());
        }
        return rs;
    }

    /**
     * 获取分享信息（二维码）
     * @param param 参数
     * @return Response<ShareInfo>
     */
    @PostMapping("/getShareInfo")
    @ApiOperation(value = "获取分享信息", notes = "获取分享信息", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response<ShareInfo> getShareInfo(@RequestBody ShareInfo param) {
        Response<ShareInfo> rs;
        try {
            // 必须传companyId、platformId、type、businessId这个几个参数，userId可以不传，所有的数据以这5个字段全匹配
            if ( null != param.getCompanyId() && null != param.getPlatformId() && null != param.getType() && null != param.getBusinessId()) {
                // notes:
                // 第一步查询对应的分享信息是不是存在
                // 第二步，信息存在返回给前端，如果不存在根据当前信息生成分享信息保存数据库，同时将二维码上传到华为云obs,将返回的地址更新到对应的数据库数据
                Response<List<ShareInfo>> infos = marketFeignService.listShareInfo(param);
                if (null != infos && infos.getCode() == 200 && null != infos.getData() && infos.getData().size() > 0) {
                    infos.getData().forEach(item -> item.setUrl("https://obs.cn-north-1.myhuaweicloud.com/obs-7d37/hh_shequ/qrcode/"+item.getUrl()));
                    // 数据存在，直接返回前端
                    rs = Response.data(infos.getData().get(0));
                } else {
                    // 数据不存在
                    String id = idGeneratorService.queryGeneratorId();
                    if (StringUtils.isNotEmpty(id)) {
                        // 设置主键ID
                        param.setId(Long.parseLong(id));
                        param.setCreatedBy("admin");
                        // 请求微信接口生成二维码，并上传到华为云，返回信息更新到数据，保存到数据库
                        Response<String> code_rs = WXCore.getUnlimitedCode(redisTemplate,APPID,SECRET,id,fileServerFeignClient);
                        if (null != code_rs && code_rs.getCode() == 200 && StringUtils.isNotEmpty(code_rs.getData())) {
                                param.setUrl(code_rs.getData());
                                param.setQrCodeName(generatorShareQrCodeName(param));
                                param.setCreatedTime(LocalDateTime.now());
                                param.setDeleted(0);
                                // 保存到数据库
                                Response<Long> save_rs = marketFeignService.saveShareInfo(param);
                                if (null != save_rs && save_rs.getCode() == 200 && null != save_rs.getData()) {
                                    // 返回最终数据
                                    param.setUrl("https://obs.cn-north-1.myhuaweicloud.com/obs-7d37/hh_shequ/qrcode/"+param.getUrl());
                                    rs = Response.data(param);
                                } else {
                                    rs = Response.failed("分享信息不存在，生成信息成功，生成信息保存到数据库失败");
                                }
                        } else {
                            rs = Response.failed("分享信息不存在，生成信息失败，调用微信接口获取二维码失败");
                        }
                    } else {
                        rs = Response.failed("分享信息不存在，生成信息失败，获取ID失败");
                    }
                }
            } else {
                rs = Response.failed("不合法请求，参数错误");
            }
        } catch (Exception e) {
            log.error("获取分享信息异常："+e.getMessage());
            rs = Response.failed("获取分享信息异常："+e.getMessage());
        }
        return rs;
    }

    /**
     * 根据scene获取分享信息
     * @param scene 二维码识别字符串
     * @return Response<ShareInfo>
     */
    @PostMapping("/getShareInfoByScene/{scene}")
    @ApiOperation(value = "根据scene获取分享信息", notes = "根据scene获取分享信息", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response<ShareInfo> getShareInfoByScene(@PathVariable("scene") String scene) {
        Response<ShareInfo> rs;
        try {
            String url = "https://obs.cn-north-1.myhuaweicloud.com/obs-7d37/hh_shequ/qrcode/";
            rs = marketFeignService.getShareInfoById(Long.parseLong(scene));
            if (null != rs && rs.getCode() == 200 && null != rs.getData()) {
                rs.getData().setUrl(url+rs.getData().getUrl());
            }
        } catch (Exception e) {
            log.error("根据scene获取分享信息异常："+e.getMessage());
            rs = Response.failed("根据scene获取分享信息异常："+e.getMessage());
        }
        return rs;
    }

    /**
     * 根据用户ID获取消费者信息
     * @param userId 用户ID
     * @return Response<ConsumerDto>
     */
    @GetMapping("/getConsumerById/{userId}")
    @ApiOperation(value = "根据用户ID获取消费者信息", notes = "根据用户ID获取消费者信息", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response<ConsumerDto> getConsumerById(@PathVariable("userId") Long userId) {
        Response<ConsumerDto> rs;
        try {
            rs = userFeignService.getConsumerById(userId);
        } catch (Exception e) {
            log.error("根据用户ID获取消费者信息异常："+e.getMessage());
            rs = Response.failed("根据用户ID获取消费者信息异常："+e.getMessage());
        }
        return rs;
    }

    /**
     * 头条登录(用户在app端点击头条登录调用头条开放平台授权登录，拿到信息以后需要存入到当前的会员信息表中)
     * @param param     头条登录参数
     * @return Response
     */
    @PostMapping("/loginByTouTiao")
    @ApiOperation(value = "头条登录", notes = "使用头条登录", consumes = MediaType.APPLICATION_JSON_VALUE)
    public Response loginByTouTiao(@RequestBody @Valid TTLoginParam param) {
        Response rs;
        try {
            String url = "https://developer.toutiao.com/api/apps/jscode2session?appid="+TTAPPID+"&secret="+TTSECRET+"&code="+ param.getCode() +"&anonymous_code="+ param.getAnonymousCode();
            RestTemplate restTemplate = new RestTemplate();
            //进行网络请求,访问url接口
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
            //根据返回值进行后续操作
            if(responseEntity.getStatusCode() == HttpStatus.OK) {
                String sessionData = responseEntity.getBody();
                //解析从微信服务器获得的openid和session_key;
                TouTiaoSession touTiaoSession = JSON.parseObject(sessionData,TouTiaoSession.class);
                if (touTiaoSession.getError() == 0 && null == touTiaoSession.getErrcode()) {
                    //获取用户的唯一标识
                    String openid = touTiaoSession.getOpenid();
                    if(StringUtils.isEmpty(openid)){
                        rs = Response.failed("请求头条接口成功，返回openId为空");
                        log.error("请求头条接口成功，返回openId为空:"+sessionData);
                    }else{
                        //获取会话秘钥
                        String session_key = touTiaoSession.getSession_key();
                        //最后要返回一个自定义的登录态（token）,用来做后续数据传输的验证
                        rs = loginService.ttLogin(param.getCompanyId(),param.getPlatformId(),param.getInvitationCode(),param.getNickname(),param.getSex(),param.getProvince(),param.getCity(),param.getCountry(),param.getHeadimgurl(),session_key,openid,TTAPPID,param.getEncryptedData(),param.getIv());
                    }
                } else {
                    rs = Response.failed(touTiaoSession.getErrmsg());
                }
            } else {
                log.error("请求头条接口失败");
                rs = Response.failed("请求头条接口失败");
            }
        } catch (Exception e) {
            log.error("头条登录出现异常"+"["+e.getMessage()+"]");
            rs = Response.failed("头条登录出现异常"+"["+e.getMessage()+"]");
        }
        return rs;
    }
    /**
     * 生成分享二维码名称
     * @param param 生成参数
     * @return String
     */
    private String generatorShareQrCodeName(ShareInfo param) {
        // 登录后分享
        if (null != param.getUserId()) {
            Response<ConsumerDto> consumerDtoResponse = userFeignService.getConsumerById(param.getUserId());
            if (null != consumerDtoResponse && consumerDtoResponse.getCode() == 200 && null != consumerDtoResponse.getData()) {
                Response<MemberDto> memberDtoResponse = userFeignService.getMemberById(consumerDtoResponse.getData().getMemberId());
                if (null != memberDtoResponse && memberDtoResponse.getCode() == 200 && null != memberDtoResponse.getData()) {
                    return formatShareQrCodeName(memberDtoResponse.getData().getNickName() != null?memberDtoResponse.getData().getNickName():memberDtoResponse.getData().getRealName(),param.getType(),param.getBusinessId());
                } else {
                    return formatShareQrCodeName(String.valueOf(consumerDtoResponse.getData().getId()),param.getType(),param.getBusinessId());
                }
            } else {
                return formatShareQrCodeName(String.valueOf(param.getUserId()),param.getType(),param.getBusinessId());
            }
        } else {
        // 未登录分享
            return formatShareQrCodeName("未登录",param.getType(),param.getBusinessId());
        }
    }

    /**
     * 格式化二维码名称
     * @param userName 用户名称
     * @param type 分享类型
     * @param businessId 业务id
     * @return String
     */
    private String formatShareQrCodeName(String userName, Integer type, Long businessId) {
        switch(type) {
            case 1: // 分享店铺
                Response<PlatStoreDTO> store_rs = userFeignService.queryById(businessId);
                if (null != store_rs && store_rs.getCode() == 200 && null != store_rs.getData()) {
                    return String.format("%s-%s-%s",userName,store_rs.getData().getStoreName(), "店铺分享码");
                } else {
                    return String.format("%s-%s-%s",userName,businessId, "店铺分享码");
                }
            case 2: // 分享商品
                Response<ExtSpuDto> goods_rs = goodsFeignService.getExtSpuById(businessId);
                if (null != goods_rs && goods_rs.getCode() == 200 && null != goods_rs.getData()) {
                    return String.format("%s-%s-%s",userName,goods_rs.getData().getTitle(), "商品分享码");
                } else {
                    return String.format("%s-%s-%s",userName,businessId, "商品分享码");
                }
            default: // 分享活动
                return "活动分享码";
        }
    }


}